package joe.works.pluginframework.support;

import java.lang.reflect.Field;

import joe.works.pluginframework.IPluginManager;
import joe.works.pluginframework.extensionpoints.StrategyExtensionPoint;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * The spring aware class does {@link ExtensionPoint} annotation processing 
 * @author josseyj
 *
 */
public class ExtensionPointAnnotationProcessor implements BeanPostProcessor, ApplicationContextAware {

	private ApplicationContext applicationContext;

	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(bean instanceof IPluginManager) {
			return bean;
		}
		Class<? extends Object> clazz = bean.getClass();
		Field[] declaredFields = clazz.getDeclaredFields();
		for (int i = 0; i < declaredFields.length; i++) {
			if(declaredFields[i].isAnnotationPresent(ExtensionPoint.class)) {
				declaredFields[i].setAccessible(true);
				try {
					Object value = declaredFields[i].get(bean);
					if(value == null) {
						loadExtensionPoint(bean, declaredFields[i], declaredFields[i].getAnnotation(ExtensionPoint.class));
					}
				} catch (Exception e) {
					//ignore
				}
			}
		}
		return bean;
	}
	
	@SuppressWarnings("unchecked")
	private void loadExtensionPoint(Object bean, Field field, ExtensionPoint extensionPointAnnotation) {
		StrategyExtensionPoint extensionPoint = new StrategyExtensionPoint();
		String strategyName = extensionPointAnnotation.strategyName();
		if(strategyName.isEmpty()) {
			strategyName = field.getName();
		}
		extensionPoint.setStrategyName(strategyName);
		extensionPoint.setStrategyType(field.getType());
		extensionPoint.setTarget(bean);
		extensionPoint.setUseCompositeStrategy(extensionPointAnnotation.useCompositeStrategy());
		extensionPoint.setUseDummy(extensionPointAnnotation.useDummy());
		getPluginManager().loadExtensionPoint(extensionPoint);
	}

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
	
	private IPluginManager getPluginManager() {
		return getBean(applicationContext, IPluginManager.class);
	}
	
	@SuppressWarnings("unchecked")
	private static <Type> Type getBean(ApplicationContext context, Class<Type> clazz) {
		String[] beanNamesForType = context.getBeanNamesForType(clazz);
		Type obj = (Type) context.getBean(beanNamesForType[0]);
		return obj;
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

}
